// src/api/http/request.ts
/**
 * 请求封装库
 */
import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { useUserStore } from "@/stores/modules/user";

import { useRouter, useRoute } from "vue-router";
import { ElNotification } from "element-plus";
import { storage } from "@/utils/storage";
import { API_URLS } from "../constants/api-url";

const BASE_URL = import.meta.env.VITE_API_BASE_URL;
const TOKEN_KEY = "accessToken";
const REFRESH_TOKEN_KEY = "refreshToken";

// 扩展 axios 类型定义
declare module "axios" {
  interface AxiosRequestConfig {
    loading?: boolean;
    showError?: boolean;
    skipAuth?: boolean;
    cache?: {
      // 是否启用缓存
      enable?: boolean;
      // 缓存键，默认使用 url + 序列化的请求参数
      key?: string;
      // 缓存时间（毫秒），默认 5 分钟
      ttl?: number;
      // 是否强制刷新缓存
      forceRefresh?: boolean;
      // 是否本地存储缓存（默认内存缓存）
      persistent?: boolean;
    };
  }
}

// 响应数据类型
interface ApiResponse<T = any> {
  code: number;
  data: T;
  message: string;
  [key: string]: any;
}

// 缓存项类型
interface CacheItem {
  data: any;
  expire: number;
}

class Request {
  private instance: AxiosInstance;
  private isRefreshing = false;
  private requests: ((token: string) => void)[] = [];
  // 内存缓存
  private memoryCache = new Map<string, CacheItem>();

  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create({
      baseURL: BASE_URL,
      ...config,
      headers: {
        "Content-Type": "application/json",
        ...config.headers,
      },
    });
    this.setupInterceptors();
  }

  /**
   * 生成缓存键
   */
  private generateCacheKey(config: AxiosRequestConfig): string {
    if (config.cache?.key) {
      return config.cache.key;
    }

    const method = config.method || "get";
    const params = method === "get" ? config.params : config.data;
    return `${method}:${config.url}:${JSON.stringify(params)}`;
  }

  /**
   * 从缓存中获取数据
   */
  private getFromCache<T>(key: string, forceRefresh = false): T | null {
    // 检查是否强制刷新
    if (forceRefresh) {
      this.memoryCache.delete(key);
      if (storage.has(key)) {
        storage.remove(key);
      }
      return null;
    }

    // 先检查内存缓存
    const memoryCacheItem = this.memoryCache.get(key);
    if (memoryCacheItem && Date.now() < memoryCacheItem.expire) {
      return memoryCacheItem.data;
    }

    // 检查持久化缓存
    if (storage.has(key)) {
      const cacheItem = storage.get<CacheItem>(key);
      if (cacheItem && Date.now() < cacheItem.expire) {
        // 同时存入内存缓存提高性能
        this.memoryCache.set(key, cacheItem);
        return cacheItem.data;
      }
    }

    return null;
  }

  /**
   * 设置缓存
   */
  private setToCache(key: string, data: any, config: AxiosRequestConfig): void {
    const ttl = config.cache?.ttl || 5 * 60 * 1000; // 默认5分钟
    const cacheItem: CacheItem = {
      data,
      expire: Date.now() + ttl,
    };

    this.memoryCache.set(key, cacheItem);

    // 如果需要持久化缓存
    if (config.cache?.persistent) {
      storage.set(key, cacheItem);
    }
  }
  private setupInterceptors() {
    // 请求拦截器
    this.instance.interceptors.request.use(async (config) => {
      const mergedConfig = {
        skipAuth: false,
        ...config,
      };
      // 检查缓存（仅 GET 请求默认支持缓存）
      if (
        mergedConfig.method?.toLowerCase() === "get" &&
        mergedConfig.cache?.enable !== false
      ) {
        const cacheKey = this.generateCacheKey(mergedConfig);
        const cachedData = this.getFromCache(
          cacheKey,
          mergedConfig.cache?.forceRefresh
        );

        if (cachedData) {
          // 返回缓存数据，并标记为从缓存获取
          return {
            ...mergedConfig,
            adapter: () =>
              Promise.resolve({
                data: cachedData,
                status: 200,
                statusText: "OK",
                headers: {},
                config: mergedConfig,
                request: {},
              }),
            fromCache: true, // 自定义标记
          };
        }
      }

      // 跳过认证的接口直接放行
      if (mergedConfig.skipAuth) {
        return mergedConfig;
      }
      // 需要认证的接口
      const accessToken = storage.get(TOKEN_KEY);
      const refreshToken = storage.get(REFRESH_TOKEN_KEY);

      // 场景1：完全无 Token（首次请求或用户未登录）
      if (!accessToken && !refreshToken) {
        throw new Error("未提供令牌，请重新登录");
      }

      // 场景2：有 Access Token，直接使用
      if (accessToken) {
        mergedConfig.headers.Authorization = `Bearer ${accessToken}`;
        return mergedConfig;
      }

      // 场景3：无 Access Token 但有 Refresh Token，尝试刷新
      if (refreshToken) {
        try {
          const { access_token, refresh_token } = await this.refreshToken(
            refreshToken as string
          );
          console.log("新令牌：", access_token, refresh_token);
          // 更新存储中的 Token
          this.setToken(access_token, refresh_token);
          mergedConfig.headers.Authorization = `Bearer ${access_token}`;
          return mergedConfig;
        } catch (error) {
          this.goToLogin("刷新令牌失败，请重新登录");
          throw error;
        }
      }

      throw new Error("无效的认证状态");
    });

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse<ApiResponse>) => {
        // 如果是缓存数据直接返回
        if ((response.config as any).fromCache) {
          return response.data;
        }
        // 允许409响应通过（不触发reject）
        if (response.status === 409) {
          return response;
        }
        // 业务逻辑错误处理
        if (response.data.code !== 200) {
          return Promise.reject(response.data);
        }

        return response.data.data; // 直接返回业务数据
      },
      async (error) => {
        return this.handleHttpError(error);
      }
    );
  }
  /**
   * 处理 HTTP 错误
   * @param error 错误信息
   * @returns
   */
  private async handleHttpError(error: any) {
    const config = error.config;
    console.log("错误信息：", error);

    // 1. 网络层错误（无响应）
    if (!error.response) {
      this.showNetworkError(error);
      return Promise.reject({
        isNetworkError: true,
        message: "网络连接失败/服务暂时不可用，请稍后重试！",
      });
    }
    const { status, data } = error.response;
    // 2. 405 方法不允许（Method Not Allowed）
    if (status === 405) {
      this.showMethodNotAllowedError(error);
      return Promise.reject({
        isMethodNotAllowed: true,
        message: "请求方法不被允许，请检查接口文档",
      });
    }
    if (error.response?.status === 409) {
      return Promise.resolve(error.response); // 改为resolve而非reject
    }

    // 2. 取消请求或跳过认证的请求
    if (axios.isCancel(error) || config?.skipAuth) {
      return Promise.reject(error);
    }

    // 3. Token 过期处理（401）
    if (status === 401) {
      return this.handleUnauthorized(error);
    }

    // 4. 服务端错误（5xx）
    if (status >= 500) {
      this.showServerError(error);
    }

    // 5. 其他错误（如 400, 403, 404 等）
    return Promise.reject(data || error);
  }
  /**
   * 错误处理
   */
  private async handleUnauthorized(error: any) {
    const userStore = useUserStore();
    const originalRequest = error.config;
    const refreshToken =
      storage.get(REFRESH_TOKEN_KEY) || userStore.refreshToken;

    // 如果是刷新 Token 请求本身失败，直接跳转登录
    if (originalRequest.url === API_URLS.REFRESH_TOKEN) {
      this.goToLogin("刷新 Token 失败");
      return Promise.reject(error);
    }

    // 无 refreshToken，直接跳转登录
    if (!refreshToken) {
      this.goToLogin("会话已过期，请重新登录");
      return Promise.reject(error);
    }

    // 正在刷新 Token，将请求加入队列
    if (this.isRefreshing) {
      return new Promise((resolve) => {
        this.requests.push((token: string) => {
          originalRequest.headers.Authorization = `Bearer ${token}`;
          resolve(this.instance(originalRequest));
        });
      });
    }

    this.isRefreshing = true;
    console.log("刷新token", refreshToken);
    try {
      const { access_token, refresh_token } = await this.refreshToken(
        refreshToken as string
      );
      // 更新存储中的 Token
      this.setToken(access_token, refresh_token);
      // 重试原始请求
      originalRequest.headers.Authorization = `Bearer ${access_token}`;
      this.requests.forEach((cb) => cb(access_token));
      this.requests = [];

      return this.instance(originalRequest);
    } catch (e) {
      this.goToLogin("会话已过期，请重新登录");
      return Promise.reject(e);
    } finally {
      this.isRefreshing = false;
    }
  }
  /**
   * 刷新token
   * @param refreshToken
   * @returns
   */
  private async refreshToken(refreshToken: string): Promise<{
    access_token: string;
    refresh_token: string;
  }> {
    console.log(refreshToken);
    try {
      const response = await this.instance.post<{
        access_token: string;
        refresh_token: string;
      }>(
        API_URLS.REFRESH_TOKEN,
        { refreshToken },
        {
          skipAuth: true,
          showError: false,
        }
      );
      console.log("刷新token成功", response, response.data);
      return response.data;
    } catch (error) {
      console.log("刷新token失败：", error);
      this.goToLogin("刷新令牌失败，请重新登录");
      throw error;
    }
  }
  /**
   * 设置令牌
   * @param token 令牌
   * @param refreshToken 刷新令牌
   */
  private setToken(token: string, refreshToken: string) {
    const userStore = useUserStore();
    userStore.setToken(token, refreshToken);
  }
  /**
   * 跳转到登录页
   */
  private goToLogin(message?: string) {
    // console.log(message);
    // return;
    const router = useRouter();
    const userStore = useUserStore();
    userStore.clearToken();
    storage.remove(TOKEN_KEY);
    storage.remove(REFRESH_TOKEN_KEY);

    router.replace(
      "/login?redirect=" +
        encodeURIComponent(router.currentRoute.value.fullPath)
    );
  }
  /**
   * 显示网络错误通知
   * @param error 错误信息
   */
  private showNetworkError(error: any) {
    ElNotification({
      title: "网络错误",
      message: "网络连接失败，请检查网络设置",
      type: "error",
    });
  }
  /**
   * 显示 405 方法不允许错误
   */
  private showMethodNotAllowedError(error: any) {
    ElNotification({
      title: "请求方法错误",
      message: "当前请求方法不被服务器允许，请检查接口文档",
      type: "error",
      duration: 5000, // 显示5秒
    });
  }

  /**
   * 显示 409 错误
   */
  private showConflictError(error: any) {
    ElNotification({
      title: "服务器错误",
      message: "服务器内部错误，请稍后再试",
      type: "error",
      duration: 5000, // 显示5秒
    });
  }
  /**
   * 显示服务器错误通知
   * @param error 错误信息
   */
  private showServerError(error: any) {
    ElNotification({
      title: "服务器错误",
      message: "服务暂时不可用，请稍后重试",
      type: "error",
    });
  }

  /**
   * 清除指定缓存
   */
  public clearCache(key: string): void {
    this.memoryCache.delete(key);
    if (storage.has(key)) {
      storage.remove(key);
    }
  }

  /**
   * 清除所有缓存
   */
  public clearAllCache(): void {
    this.memoryCache.clear();
    // 注意：这里会清除所有 storage 数据，如果有其他存储数据需要保留，需要改进
    storage.clear();
  }

  public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.instance.get(url, config);
  }

  public post<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.post(url, data, config);
  }

  public put<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.put(url, data, config);
  }

  public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.instance.delete(url, config);
  }
  public async upload<T = any>(
    url: string,
    file: File | FormData,
    params?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    // 自动处理 File 转 FormData
    const formData = file instanceof File ? new FormData() : file;
    if (file instanceof File) {
      formData.append("file", file);
    }
    // 动态计算超时时间（示例：每MB增加1秒，最低30秒）
    const calculateTimeout = (size: number) => {
      const minTimeout = 30 * 1000; // 基础超时30秒
      const sizeInMB = size / (1024 * 1024);
      return Math.min(
        10 * 60 * 1000, // 最大不超过10分钟
        Math.max(minTimeout, sizeInMB * 1000) // 每MB增加1秒
      );
    };
    // 合并配置（强制 multipart/form-data）
    const mergedConfig = {
      timeout: calculateTimeout(file instanceof File ? file.size : 0),
      ...config,
      headers: {
        "Content-Type": "multipart/form-data",
        ...config?.headers,
      },
    };

    return this.instance.post(url, formData, mergedConfig);
  }
}

export const http = new Request({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 120 * 1000,
});
